package com.yc.springioc.core;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

/**
 * 实例化容器
 * @author yy
 * date 2020年12月20日
 * QQ:1464080002
 */
public class YcClassPathXmlApplicationContext {
	private List<Bean> beans = new ArrayList<Bean>();
	private Map<String, Object> singtons = new HashMap<String, Object>(); //存放实例化后的对象
	
	public YcClassPathXmlApplicationContext(String ... files) {
		init(files);
	}

	private void init(String... files) {
		if(files == null || files.length <= 0) {
			return;
		}
		
		for (String file : files) {
			pareseXml(file);
		}
		//实例化对象
		try {
			makeInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		singtons.forEach((key, val) -> {
			System.out.println(key + ":" + val);
		});
	}

	/**
	 * 解析xml
	 */
	@SuppressWarnings("unchecked")
	private void pareseXml(String file) {
		SAXReader read = new SAXReader();
		Document doc = null;
		
		try(InputStream is = this.getClass().getClassLoader().getResourceAsStream(file)){
			doc = read.read(is);
			
			//设置命名空间
			Map<String, String> nsmap = new HashMap<String, String>();
			nsmap.put("yc", "http://www.springframework.org/schema/beans");
			XPath xpath = doc.createXPath("//yc:beans/yc:bean");
			
			xpath.setNamespaceURIs(nsmap);
			List<Element> beans = xpath.selectNodes(doc);//从doc文档中查找
			if(beans == null || beans.isEmpty()) {
				return;
			}
			
			String id = null;
			Property prop = null;
			Bean bean = null;
			List<Element> childs = null;
			List<Property> properties = null;
			
			for (Element el : beans) {
				bean = new Bean();
				id = el.attributeValue("id");
				bean.setId(id);
				bean.setClassName(el.attributeValue("class"));
				
				//获取这个对象中要注值的属性
				childs = el.elements(); //获取子元素
				if(childs == null || childs.isEmpty()) {
					continue;
				}
				properties = new ArrayList<Property>();
				for (Element e : childs) {
					prop = new Property();
					prop.setName(e.attributeValue("name"));
					prop.setValue(e.attributeValue("value"));
					prop.setRef(e.attributeValue("ref"));
					properties.add(prop);
				}
				bean.setProperties(properties);
				this.beans.add(bean);
			}
		}catch (IOException e1) {
			e1.printStackTrace();
		}catch (DocumentException e) {
			e.printStackTrace();
		} 
	}
	
	/**
	 * 实例化对象
	 * @throws Exception 
	 */
	private void makeInstance() throws Exception {
		if(beans == null || beans.isEmpty()) {//说明没有实体类需要自我管理
			return;
		}
		
		//存放哪些对象 -> 有属性需要ref注值的对象
		Map<Object, Map<String, String>> refInjection = new HashMap<Object, Map<String,String>>();
		Map<String, Method> setters = null; //获取所有的setter方法 
		
		Class c = null;
		Object obj = null;
		List<Property> properties = null;
		Map<String, String> mapAttr = null;
		String value = null;
		Method method = null; //需要调用的方法
		String name = null; //属性名
		Class[] types = null; //这个方法的形参列表
		Class type = null;//这个方法的第一个形参类型
		
		for (Bean bean : beans) {
			c = Class.forName(bean.getClassName());//加载类
			setters = getSetter(c);//获取所有的setter方法
			obj = c.newInstance(); //实例化对象
			
			properties = bean.getProperties();
			if(properties == null || properties.isEmpty()) {
				singtons.put(bean.getId(), obj);
				continue;
			}
			
			if(setters == null || setters.isEmpty()) {
				continue;
			}
			
			mapAttr = new HashMap<String, String>();
			//如果有属性要注值
			for (Property prop : properties) {//注意先不管ref注入
				value = prop.getRef();
				name = prop.getName();
				name = "set" + name.substring(0,1).toUpperCase() + name.substring(1);
				if(value!= null && !"".equals(value)) {
					mapAttr.put(name, value);
					continue;
				}
				
				//说明是普通注值
				method = setters.getOrDefault(name, null);//根据方法名查找相应的方法
				if(method == null ) {//说明没有找到相应的setter方法
					continue;
				}
				
				types = method.getParameterTypes();
				if(types == null || types.length <= 0) {//说明没带参数
					continue;
				}
				
				type = types[0];//获取第一个参数
				value = prop.getValue();
				if(type == Integer.class || type == Integer.TYPE) {
					method.invoke(obj, Integer.parseInt(value));
				}else if(type == Double.class || type == Double.TYPE) {
					method.invoke(obj, Double.parseDouble(value));
				}else if(type == Float.class || type == Float.TYPE) {
					method.invoke(obj, Float.parseFloat(value));
				}else {
					method.invoke(obj, value);
				}
			}
				refInjection.put(obj, mapAttr);
				singtons.put(bean.getId(), obj);
			}
			//判断有没有ref注值
			if(refInjection == null || refInjection.isEmpty()) {
				return;
			}
		}
		
		/**
		 * 获取指定类的所有setter方法
		 */
		private Map<String, Method> getSetter(Class<?> c) {
			Method[] methods = c.getDeclaredMethods();
			if(methods == null || methods.length <= 0) {
				return null;
			}
			
			Map<String, Method> setters = new HashMap<String, Method>();
			for (Method method : methods) {
				if(method.getName().startsWith("set")) {
					setters.put(method.getName(), method);
				}
			}
			return setters;
		}
	

	/**
	 * 根据bean的id获取对象
	 * @param keyId
	 * @return
	 */
	public Object getBean(String keyId) {
		return singtons.getOrDefault(keyId, null);
	}
}
